package com.xuan.yupao.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.xuan.yupao.constant.UserConstant;
import com.xuan.yupao.manager.OnlineUserManager;
import com.xuan.yupao.model.domain.Message;
import com.xuan.yupao.model.domain.PrivateChatSession;
import com.xuan.yupao.model.domain.TeamUser;
import com.xuan.yupao.model.domain.User;
import com.xuan.yupao.model.domain.request.WebSocketRequest;
import com.xuan.yupao.model.domain.vo.FromUser;
import com.xuan.yupao.model.domain.vo.MessageVo;
import com.xuan.yupao.service.MessageService;
import com.xuan.yupao.service.PrivateChatSessionService;
import com.xuan.yupao.service.TeamUserService;
import com.xuan.yupao.service.UserService;
import com.xuan.yupao.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Component
public class WebSocketAPI extends TextWebSocketHandler {

    @Resource
    private OnlineUserManager userManager;

    @Resource
    private UserService userService;

    @Resource
    private PrivateChatSessionService chatSessionService;

    @Resource
    private MessageService messageService;

    @Resource
    private TeamUserService teamUserService;

    private final Gson gson = new Gson();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 这个方法在 WebSocket 连接建立完成后被调用
        User user = (User) session.getAttributes().get(UserConstant.USER_SESSION);
        if (userManager.getWebSocketSession(user.getId()) != null) {
            userManager.getWebSocketSession(user.getId()).close();
        }
        userManager.online(user.getId(), session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        User loginUser = (User) session.getAttributes().get(UserConstant.USER_SESSION);
        WebSocketRequest webSocketRequest = gson.fromJson(message.getPayload(), new TypeToken<WebSocketRequest>() {
        }.getType());
        log.info("[handleTextMessage] webSocketRequest:{}", webSocketRequest);

        // 1. 构造要转发的信息
        MessageVo messageVo = new MessageVo();
        User user = userService.getById(webSocketRequest.getFromId());
        FromUser fromUser = new FromUser();
        BeanUtils.copyProperties(user, fromUser);
        messageVo.setFromUser(fromUser);
        messageVo.setText(webSocketRequest.getText());
        String date = DateUtils.getDateTime(new Date());
        messageVo.setCreateTime(date);
        messageVo.setChatType(webSocketRequest.getChatType());
        String respJson = gson.toJson(messageVo);

        // 2. 构造存入数据库的 Message对象, 并向聊天房间中在线的成员转发消息
        Integer chatType = webSocketRequest.getChatType();
        Message insertMessage = new Message();
        switch (chatType) {
            case 1: {
                insertMessage = transferPrivateChat(webSocketRequest, respJson, loginUser.getId());
                break;
            }
            case 2: {
                insertMessage = transferTeamChat(webSocketRequest, respJson, loginUser.getId());
                break;
            }
            case 3: {
                insertMessage = transferHallChat(webSocketRequest, respJson, session);
                break;
            }
        }
        // 3. 往数据库中插入消息记录
        messageService.save(insertMessage);
    }

    private Message transferHallChat(WebSocketRequest webSocketRequest, String respJson, WebSocketSession currentSession) {
        // 1. 遍历哈希表,存在哈希表中的 session 即为在线的用户(自己不发送)
        for (WebSocketSession webSocketSession : userManager.getUserManagerMap().values()) {
            if (currentSession == webSocketSession) {
                continue;
            }
            try {
                webSocketSession.sendMessage(new TextMessage(respJson));
            } catch (IOException e) {
                log.error("大厅聊天消息转发失败");
            }
        }

        // 2. 构造要插入的数据
        Message message = new Message();
        message.setSendId(webSocketRequest.getFromId());
        message.setContent(webSocketRequest.getText());
        message.setChatType(3);
        return message;
    }

    private Message transferTeamChat(WebSocketRequest webSocketRequest, String respJson, Long loginUserId) {
        // 1. 获取队伍中所有成员的 id
        Long teamId = webSocketRequest.getTeamId();
        QueryWrapper<TeamUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("team_id", teamId);
        List<Long> userIdList = teamUserService.list(queryWrapper).stream().map(TeamUser::getUserId).collect(Collectors.toList());
        // 2. 向在线的成员转发消息 (不包括自身,因为前端在通信前,已经向自身发送了消息)
        for (Long userId : userIdList) {
            if (Objects.equals(userId, loginUserId)) {
                continue;
            }
            WebSocketSession socketSession = userManager.getWebSocketSession(userId);
            if (socketSession == null) {
                continue;
            }
            // 转发消息
            try {
                socketSession.sendMessage(new TextMessage(respJson));
            } catch (IOException e) {
                log.error("队伍聊天转发失败! webSocketRequest:{}", webSocketRequest);
            }
        }
        // 3. 构造要插入数据库的消息对象
        Message message = new Message();
        message.setSendId(webSocketRequest.getFromId());
        message.setTeamId(teamId);
        message.setContent(webSocketRequest.getText());
        return message;
    }

    private Message transferPrivateChat(WebSocketRequest webSocketRequest, String respJson, Long loginUserId) {
        // 1. 若私聊用户在线,则转发消息
        Long toId = webSocketRequest.getToId();
        WebSocketSession session = userManager.getWebSocketSession(toId);
        if (session != null) {
            try {
                session.sendMessage(new TextMessage(respJson));
            } catch (IOException e) {
                log.error("私聊消息转发失败! webSocketRequest:{}", webSocketRequest);
            }
        }

        // 2. 查看两个用户间此前是否存在聊天会话(ChatSession),若不存在则在数据表(chat_session)新增记录
        QueryWrapper<PrivateChatSession> queryWrapper = new QueryWrapper<>();
        queryWrapper.and(wrapper -> wrapper.eq("user_id", toId).eq("another_user_id", loginUserId))
                .or(wrapper -> wrapper.eq("user_id", loginUserId).eq("another_user_id", toId));
        PrivateChatSession chatSession = chatSessionService.getOne(queryWrapper);
        if (chatSession == null) {
            // 不存在私聊会话,新增记录
            chatSession = new PrivateChatSession();
            chatSession.setUserId(loginUserId);
            chatSession.setAnotherUserId(toId);
            chatSessionService.save(chatSession);
        }
        Message message = new Message();
        message.setSendId(loginUserId);
        message.setReceiveId(toId);
        message.setSessionId(chatSession.getId());
        message.setContent(webSocketRequest.getText());
        return message;
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        // 这个方法在连接出现异常时被调用
        log.info("[WebSocket] 连接异常:{}", exception.getMessage());
        User user = (User) session.getAttributes().get(UserConstant.USER_SESSION);
        userManager.offline(user.getId(), session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        // 这个方法在连接断开后被调用
        User user = (User) session.getAttributes().get(UserConstant.USER_SESSION);
        userManager.offline(user.getId(), session);
    }


//    private WebsocketVo handleError(String error) {
//        WebsocketVo websocketVo = new WebsocketVo();
//        websocketVo.setError(error);
//        return websocketVo;
//    }
}
